'use client';

import {
  type ChangeEvent,
  type FormEvent,
  useContext,
  useEffect,
  useRef,
  useState,
} from 'react';
import type {
  ICreateUserByEmailBody,
  ICreateUserByPhoneBody,
  ICreateUserByUsernameBody,
  IDifference,
} from '@/interfaces';
import type { TBody } from '@/types';
import {
  createUserByEmail,
  createUserByPhone,
  createUserByUsername,
  queryAccountPublicKey,
  queryPasswordPublicKey,
} from '@/services/api';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import classNames from 'classnames';
import { nanoid } from 'nanoid';
import type JSEncrypt from 'jsencrypt';
import diff from 'microdiff';
import useToast from '@/hooks/useToast';
import { UserAdminContext } from '@/contexts/admin/user';
import {
  cleanFormData,
  getDiffData,
  getFilterData,
  isEmpty,
  isPhone,
} from '@/lib/tool';

export default function CreateUserAdminPage(this: any) {
  const [type, setType] = useState<'username' | 'phone' | 'email'>('username');
  const { show } = useToast();
  const context = useContext(UserAdminContext);
  const queryClient = useQueryClient();
  const jsEncryptRef = useRef<JSEncrypt | null>(null);

  function onClickTab(type: 'username' | 'phone' | 'email') {
    setType(type);
  }

  async function getJsEncrypt(): Promise<JSEncrypt> {
    let jsEncrypt;
    if (jsEncryptRef.current) {
      jsEncrypt = jsEncryptRef.current;
    } else {
      const JSEncrypt = (await import('jsencrypt')).JSEncrypt;
      jsEncrypt = new JSEncrypt();
      jsEncryptRef.current = jsEncrypt;
    }
    return jsEncrypt;
  }

  function onClickReturn() {
    if (typeof context.setIsCreate === 'function') {
      if (context.queryKey) {
        try {
          queryClient.refetchQueries(
            { queryKey: context.queryKey },
            { throwOnError: true },
          );
        } catch (e) {
          show({
            type: 'DANGER',
            message: e,
          });
        }
      }

      context.setIsCreate(false);
    }
  }

  return (
    <div className="row">
      <div className="col">
        <div className="card">
          <div className="card-header bg-transparent text-muted">
            <div className="row row-cols-auto justify-content-between">
              <div className="col">
                <div>
                  <i className="bi bi-person-plus me-2"></i>
                  新增用户
                </div>
              </div>
              <div className="col">
                <div>
                  <i
                    onClick={onClickReturn}
                    className="bi bi-arrow-return-left cursor-pointer text-primary"
                  ></i>
                </div>
              </div>
            </div>
          </div>
          <div className="card-body">
            <div className="vstack gap-4">
              <div className="row">
                <div className="col">
                  <nav className="nav nav-pills flex-column flex-sm-row">
                    <a
                      onClick={onClickTab.bind(this, 'username')}
                      className={classNames(
                        'flex-sm-fill text-sm-center nav-link cursor-pointer',
                        {
                          'bg-light': type === 'username',
                        },
                      )}
                    >
                      用户名
                    </a>
                    <a
                      onClick={onClickTab.bind(this, 'phone')}
                      className={classNames(
                        'flex-sm-fill text-sm-center nav-link cursor-pointer',
                        {
                          'bg-light': type === 'phone',
                        },
                      )}
                    >
                      手机号
                    </a>
                    <a
                      onClick={onClickTab.bind(this, 'email')}
                      className={classNames(
                        'flex-sm-fill text-sm-center nav-link cursor-pointer',
                        {
                          'bg-light': type === 'email',
                        },
                      )}
                    >
                      邮箱
                    </a>
                  </nav>
                </div>
              </div>

              {type === 'username' && <Username getJsEncrypt={getJsEncrypt} />}

              {type === 'phone' && <Phone getJsEncrypt={getJsEncrypt} />}

              {type === 'email' && <Email getJsEncrypt={getJsEncrypt} />}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

const Username = ({
  getJsEncrypt,
}: {
  getJsEncrypt: () => Promise<JSEncrypt>;
}) => {
  const [differenceData, setDifferenceData] = useState<IDifference[]>([]);
  const { show } = useToast();
  const [form, setForm] = useState({
    username: '',
    password: '',
  });
  const [randomPassword, setRandomPassword] = useState('');

  const createUserByUsernameMutation = useMutation(
    async (variables: TBody<ICreateUserByUsernameBody>) => {
      await createUserByUsername(variables);
    },
  );

  const queryPasswordPublicKeyQuery = useQuery(
    ['/key', '/public', '/password'],
    async (context) => {
      return (await queryPasswordPublicKey()) as string;
    },
    {
      enabled: false,
    },
  );

  useEffect(() => {
    setDifferenceData(
      diff(
        {
          username: '',
          password: '',
        },
        form,
      ),
    );
  }, [form]);

  async function onSubmit(e: FormEvent<HTMLFormElement>) {
    try {
      e.preventDefault();
      e.stopPropagation();

      checkForm();
      const body = getFilterData(getDiffData(differenceData));
      if (isEmpty(body)) {
        show({
          type: 'DANGER',
          message: '新增用户失败，数据不存在',
        });
        return;
      }

      const encryptedPassword = await getEncryptedPassword(body.password);
      await createUserByUsernameMutation.mutateAsync({
        data: {
          alias: body.alias,
          username: body.username,
          password: encryptedPassword,
        },
      });

      clearData();
      show({
        type: 'SUCCESS',
        message: '保存完成',
      });
    } catch (e) {
      createUserByUsernameMutation.reset();
      show({
        type: 'DANGER',
        message: e,
      });
    }
  }

  function clearData() {
    setForm(cleanFormData(form));
    setDifferenceData([]);
  }

  async function getEncryptedPassword(password: string): Promise<string> {
    const jsEncrypt = await getJsEncrypt();
    const result = await queryPasswordPublicKeyQuery.refetch({
      throwOnError: true,
    });
    const publicKey = result.data;
    if (!publicKey) {
      throw '获取密码数据失败';
    }

    jsEncrypt.setPublicKey(publicKey);
    const encryptedData = jsEncrypt.encrypt(password);
    if (!encryptedData) {
      throw '加密密码数据失败';
    }

    return encryptedData;
  }

  function onClickGenerateRandomPassword() {
    const rPassword = nanoid();
    const password = rPassword.substring(0, Math.floor(rPassword.length / 2));
    setRandomPassword(password);
    setForm({ ...form, password });
  }

  function checkForm() {
    const { username, password } = form;

    if (!username) {
      throw '用户名不能为空';
    }

    if (!password) {
      throw '密码不能为空';
    }
  }

  function onChange(e: ChangeEvent<HTMLInputElement>) {
    const name = e.target.name;
    const value = e.target.value;
    setForm({ ...form, [name]: value });
  }

  return (
    <div className="row">
      <div className="col">
        <form onSubmit={onSubmit} className="vstack gap-4">
          <div>
            <label className="form-label">用户名</label>
            <input
              type="text"
              name="username"
              value={form.username}
              onChange={onChange}
              className="form-control"
              aria-describedby="username"
              placeholder="请输入用户名，长度最小 3，最大 16"
            />
          </div>

          <div>
            <label className="form-label">
              密码
              {randomPassword && (
                <span className="mx-2 text-muted">
                  (随机密码：{randomPassword}，请注意保存，只会显示一次)
                </span>
              )}
            </label>
            <div className="input-group">
              <input
                type="password"
                name="password"
                value={form.password}
                onChange={onChange}
                className="form-control"
                autoComplete="password"
                placeholder="请输入密码，长度最小 6，最大 19"
              />
              <button
                onClick={onClickGenerateRandomPassword}
                className="btn btn-outline-secondary"
                type="button"
              >
                生成随机密码
              </button>
            </div>
          </div>

          <button
            type="submit"
            disabled={createUserByUsernameMutation.isLoading}
            className="btn btn-primary w-25 my-4"
          >
            {createUserByUsernameMutation.isLoading && (
              <span
                className="spinner-border spinner-border-sm me-2"
                role="status"
                aria-hidden="true"
              ></span>
            )}
            保存
          </button>
        </form>
      </div>
    </div>
  );
};

const Phone = ({
  getJsEncrypt,
}: {
  getJsEncrypt: () => Promise<JSEncrypt>;
}) => {
  const [differenceData, setDifferenceData] = useState<IDifference[]>([]);
  const { show } = useToast();
  const [form, setForm] = useState({
    phone: '',
  });

  const createUserByPhoneMutation = useMutation(
    async (variables: TBody<ICreateUserByPhoneBody>) => {
      await createUserByPhone(variables);
    },
  );

  const queryAccountPublicKeyQuery = useQuery(
    ['/key', '/public', '/account'],
    async (context) => {
      return (await queryAccountPublicKey(context)) as string;
    },
    {
      enabled: false,
    },
  );

  useEffect(() => {
    setDifferenceData(
      diff(
        {
          phone: '',
        },
        form,
      ),
    );
  }, [form]);

  async function onSubmit(e: FormEvent<HTMLFormElement>) {
    try {
      e.preventDefault();
      e.stopPropagation();

      checkForm();
      const body = getFilterData(getDiffData(differenceData));
      if (isEmpty(body)) {
        show({
          type: 'DANGER',
          message: '新增用户失败，数据不存在',
        });
        return;
      }

      const encryptedAccount = await getEncryptedAccount(body.phone);
      await createUserByPhoneMutation.mutateAsync({
        data: {
          alias: body.alias,
          phone: encryptedAccount,
        },
      });

      clearData();
      show({
        type: 'SUCCESS',
        message: '保存完成',
      });
    } catch (e) {
      createUserByPhoneMutation.reset();
      show({
        type: 'DANGER',
        message: e,
      });
    }
  }

  function clearData() {
    setForm(cleanFormData(form));
    setDifferenceData([]);
  }

  async function getEncryptedAccount(account: string): Promise<string> {
    const jsEncrypt = await getJsEncrypt();
    const result = await queryAccountPublicKeyQuery.refetch({
      throwOnError: true,
    });
    const publicKey = result.data;
    if (!publicKey) {
      throw '获取账号数据失败';
    }

    jsEncrypt.setPublicKey(publicKey);
    const encryptedData = jsEncrypt.encrypt(account);
    if (!encryptedData) {
      throw '加密账号数据失败';
    }

    return encryptedData;
  }

  function checkForm() {
    const { phone } = form;

    if (!phone) {
      throw '手机号不能为空';
    }

    if (!isPhone(phone)) {
      throw '请正确输入手机号';
    }
  }

  function onChange(e: ChangeEvent<HTMLInputElement>) {
    const name = e.target.name;
    const value = e.target.value;
    setForm({ ...form, [name]: value });
  }

  return (
    <div className="row">
      <div className="col">
        <form onSubmit={onSubmit} className="vstack gap-4">
          <div>
            <label className="form-label">手机号</label>
            <input
              type="tel"
              name="phone"
              value={form.phone}
              onChange={onChange}
              className="form-control"
              aria-describedby="phone"
              placeholder="请输入手机号"
            />
          </div>

          <button
            type="submit"
            disabled={createUserByPhoneMutation.isLoading}
            className="btn btn-primary w-25 my-4"
          >
            {createUserByPhoneMutation.isLoading && (
              <span
                className="spinner-border spinner-border-sm me-2"
                role="status"
                aria-hidden="true"
              ></span>
            )}
            保存
          </button>
        </form>
      </div>
    </div>
  );
};

const Email = ({
  getJsEncrypt,
}: {
  getJsEncrypt: () => Promise<JSEncrypt>;
}) => {
  const [differenceData, setDifferenceData] = useState<IDifference[]>([]);
  const { show } = useToast();
  const [form, setForm] = useState({
    email: '',
  });

  const createUserByEmailMutation = useMutation(
    async (variables: TBody<ICreateUserByEmailBody>) => {
      await createUserByEmail(variables);
    },
  );

  const queryAccountPublicKeyQuery = useQuery(
    ['/key', '/public', '/account'],
    async (context) => {
      return (await queryAccountPublicKey(context)) as string;
    },
    {
      enabled: false,
    },
  );

  useEffect(() => {
    setDifferenceData(
      diff(
        {
          email: '',
        },
        form,
      ),
    );
  }, [form]);

  async function onSubmit(e: FormEvent<HTMLFormElement>) {
    try {
      e.preventDefault();
      e.stopPropagation();

      checkForm();
      const body = getFilterData(getDiffData(differenceData));
      if (isEmpty(body)) {
        show({
          type: 'DANGER',
          message: '新增用户失败，数据不存在',
        });
        return;
      }

      const encryptedAccount = await getEncryptedAccount(body.email);
      await createUserByEmailMutation.mutateAsync({
        data: {
          alias: body.alias,
          email: encryptedAccount,
        },
      });

      clearData();
      show({
        type: 'SUCCESS',
        message: '保存完成',
      });
    } catch (e) {
      createUserByEmailMutation.reset();
      show({
        type: 'DANGER',
        message: e,
      });
    }
  }

  function clearData() {
    setForm(cleanFormData(form));
    setDifferenceData([]);
  }

  async function getEncryptedAccount(account: string): Promise<string> {
    const jsEncrypt = await getJsEncrypt();
    const result = await queryAccountPublicKeyQuery.refetch({
      throwOnError: true,
    });
    const publicKey = result.data;
    if (!publicKey) {
      throw '获取账号数据失败';
    }

    jsEncrypt.setPublicKey(publicKey);
    const encryptedData = jsEncrypt.encrypt(account);
    if (!encryptedData) {
      throw '加密账号数据失败';
    }

    return encryptedData;
  }

  function checkForm() {
    const { email } = form;

    if (!email) {
      throw '电子邮箱不能为空';
    }

    if (!email.includes('.') || !email.includes('@')) {
      throw '请正确输入电子邮箱';
    }
  }

  function onChange(e: ChangeEvent<HTMLInputElement>) {
    const name = e.target.name;
    const value = e.target.value;
    setForm({ ...form, [name]: value });
  }

  return (
    <div className="row">
      <div className="col">
        <form onSubmit={onSubmit} className="vstack gap-4">
          <div>
            <label className="form-label">电子邮箱</label>
            <input
              type="email"
              name="email"
              value={form.email}
              onChange={onChange}
              className="form-control"
              aria-describedby="email"
              placeholder="请输入电子邮箱"
            />
          </div>

          <button
            type="submit"
            disabled={createUserByEmailMutation.isLoading}
            className="btn btn-primary w-25 my-4"
          >
            {createUserByEmailMutation.isLoading && (
              <span
                className="spinner-border spinner-border-sm me-2"
                role="status"
                aria-hidden="true"
              ></span>
            )}
            保存
          </button>
        </form>
      </div>
    </div>
  );
};
